package com.nulldev.util.web.nTubeExtensions.yte_for_archives;

import java.util.List;
import java.util.Map;

import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.cntft.ControllableFutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.io.file.FileUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.URLUtils.Entry;
import com.nulldev.util.web.YouTubeExtractor.YoutubeExtractionOptions;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractorConstants;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata.PlaybackMode;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractedVideo;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractionResult;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;
import com.nulldev.util.web.legacyURI.LegacyURIUtils;
import com.nulldev.util.web.nTube.NTubeServer;

public class YTEArchiveExtractor extends IYouTubeExtractor {

	public static final IYouTubeExtractor INSTANCE = new YTEArchiveExtractor();

	public static final String MAP_TYPE_NON_STANDARD_ARCHIVE_DATE = "--archive-date";
	public static final String MAP_TYPE_NON_STANDARD_USE_NTUBE_ARCHIVE_PROXY = "--archive-proxy";

	/* */

	private static final Logger logger = LoggerFactory.getLoggerD(true, true);

	@SuppressWarnings("deprecation")
	@Override
	public Executable<ExtractedVideo> generateTask(String videoID, YoutubeExtractionOptions... options) {
		return generateTask(videoID, null, options);
	}

	@SuppressWarnings("deprecation")
	@Override
	public Executable<ExtractedVideo> generateTask(String videoID, String extraArgs, YoutubeExtractionOptions... options) {
		logger.warn("generateTask(String, String, YoutubeExtractionOptions) is deprecated and marked for removal.");
		return generateTask(IYouTubeExtractorConstants.convertFromLegacy(videoID, extraArgs, options));
	}

	@Override
	public Executable<ExtractedVideo> generateTask(final Map<String, Object> arguments) {
		if (!isValidMap(arguments))
			throw new IllegalArgumentException("Invalid map: " + arguments);
		final String videoID = (String) arguments.get(MAP_TYPE_VIDEO_ID);
		if (!isValidVideoID(videoID))
			throw new IllegalArgumentException("Invalid video ID: " + videoID);
		if (!arguments.containsKey(MAP_TYPE_NON_STANDARD_ARCHIVE_DATE))
			throw new IllegalArgumentException("Missing archive date!");
		final boolean useProxy = isEnabled(arguments, MAP_TYPE_NON_STANDARD_USE_NTUBE_ARCHIVE_PROXY);
		final String date = (String) arguments.get(MAP_TYPE_NON_STANDARD_ARCHIVE_DATE);
		final ControllableFutureTask<ExtractedVideo> cnft = new ControllableFutureTask<ExtractedVideo>();
		final String url = "https://web.archive.org/web/" + date + "/https://www.youtube.com/watch?v=" + videoID;
		final Entry[] _args =
			{ new Entry("Via", "nTube " + NTubeServer.nTube_RELEASE_STRING) };
		try {
//			HttpClient.getString().url(url).timeout(Duration.ofSeconds(24)).headers(_args).createTaskAndRun()
//					.addCallback(new AsyncTaskCallback<HttpResponse<String>>() {
//
//						@Override
//						public void onCompletion(final HttpResponse<String> result) {
//							if (result.status() == 404) {
//								cnft.complete(new ExtractedVideo(Lists.of(), null, ExtractionResult.FAILED).setMessage("VIDEO_NOT_FOUND"));
//								return;
//							}
//							JVM.println(result.dataThenClose());
//						}
//
//						@Override
//						public void onError(Throwable e) {
//							cnft.fail(e);
//						}
//
//					});
			final String data = FileUtil.getResourceAsString(YTEArchiveExtractor.class, "tmp.txt");
			final List<FmtStreamMap> fmts = YTEArchiveExtractorImpl.call(data);
			if (fmts.isEmpty()) {
				cnft.complete(new ExtractedVideo(Lists.of(), null, ExtractionResult.FAILED).setMessage("NO_FORMATS_FOUND"));
				return cnft;
			}
			final List<YouTubeVideo> converted = FastLists.list(fmts.size());
			fmts.forEach(fmt -> {
				/*
				 * There is a 0.0000001% chance that IA has archived anything using
				 * non-fallback.
				 */
				if (StringsUtil.isBlank(fmt.fallbackHost))
					return;
				final String chost = StringsUtil.substringBetweenO(fmt.url, "://", "/videoplayback");
				final String _furl = fmt.url.replace("//" + chost + "/videoplayback", "//" + fmt.fallbackHost + "/videoplayback");
				/**
				 * TODO: Our URLs differ, um...
				 *
				 * What we get: <br>
				 * "http://tc.v24.cache4.googlevideo.com/videoplayback?sver=3&sparams=id%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&expire=1393506102&upn=xHHbq79aZ4E&ip=207.241.237.107&ms=au&itag=22&mv=m&source=youtube&key=yt5&id=85729ca238fb564e&fexp=923901%2C910207%2C914070%2C937417%2C937416%2C913434%2C936910%2C936913%2C902907&ipbits=0&mt=1393484231&ratebypass=yes"
				 * What IA uses: <br>
				 * "http://v24.lscache4.c.youtube.com/videoplayback?sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Cratebypass&itag=22&ip=207.0.0.0&signature=5EAB4EB9A0FAD87F92A50C8BC0925962884F498D.11077055D0784EB312D43444383DCFE23187C6D6&sver=3&ratebypass=yes&expire=1312264800&key=yt1&ipbits=8&id=85729ca238fb564e"
				 */
				final String furl = useProxy ? "/archives/proxy?url=" + LegacyURIUtils.safeEncode(_furl) : _furl;
				converted.add(new YouTubeVideo(furl, !fmt.format.hasAudio(), Integer.parseInt(fmt.itag), fmt.format));
			});
			cnft.complete(new ExtractedVideo(converted,
					new YouTubeMetadata(videoID, "title", "author", "authorId", -1, -1, 5.0d, -1, -1, false, new String[0], Lists.of())
							.setPlaybackMode(PlaybackMode.NTUBE_ARCHIVE_VIEWER),
					ExtractionResult.SUCCESS));
		} catch (Exception _ex) {
			cnft.fail(_ex);
		}
		return cnft;
	}

	@Override
	public String getID() {
		return "ntube.archive_yte";
	}

	@Override
	public int abiVersion() {
		return 3000;
	}

	@Override
	public Object getFlag(final int flag) {
		switch (flag) {
			case FLAG_EXTRACTOR_SUPPORTS_ABI_2_2:
				return FLAG_TRUE;
			case FLAG_EXTRACTOR_GET_SUPPORTED_ABI:
				return 2.2f;
			default:
				return FLAG_NOT_SUPPORTED;
		}
	}

}
